# -*-coding: Utf-8 -*-
# @File : common .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time：2024/5/7
import logging
import random
import hashlib
from logging import config
from typing import Optional, Tuple, Union, Any
from conf import settings
import os
import time
import uuid


class VerifyFormat:

    def is_number(self, number: str) -> Tuple[bool, Union[int, str]]:
        '''

        :param number: 输入的内容校验是否符合整数格式
        :return: 是否为数字 + 错误信息 / 是否为数字 + 数字
        '''
        if not number.isdigit():
            return False, f'当前格式不正确!必须是数字格式'
        number = int(number)
        if number < 0:
            return False, f'当前格式不正确,必须大于0!'
        return True, number

    def verify_login_password(self, password):
        """
        验证登录密码是否符合规范：必须包含至少一个数字和一个字母。

        参数:
        - password (str): 用户输入的密码字符串。

        返回:
        - bool: 如果密码合法返回True，否则返回False。
        """
        # 检验当前密码必须符合6位长度
        if len(password) < 6:
            return False, f'密码长度不合法!必须是6位!'
        # 检查密码中是否至少包含一个数字和一个字母
        has_digit = any(char.isdigit() for char in password)
        has_letter = any(char.isalpha() for char in password)

        # 如果同时包含数字和字母，则密码合格
        if has_digit and has_letter:
            return True, password
        else:
            # 可以根据需要添加具体的错误信息提示
            return False, "密码必须包含至少一个数字和一个字母!"


class CommonInterface(object):
    def __init__(self):
        self.tools = Tools()

    def register_interface(self, cls: Any, role: str, username: str, password: str) -> bool:
        '''
        注册接口
        :param cls: 当前调用模型类
        :param role: 用户角色信息
        :param username: 用户输入的用户名
        :param password: 用户输入的密码
        :return: 注册成功失败,原因
        '''
        # 【一】获取所有用户的用户名列表
        flag, username_list = cls.show_name()
        if not flag:
            # 【1】记录日志
            self.tools.log_info(tag='warning', role=role, username=username,
                                info=f'注册失败!原因是数据为空!')
            return False
        # 【二】判断当前用户是否注册过
        if username in username_list:
            self.tools.log_info(tag='warning', role=role, username=username,
                                info=f'注册失败!原因是已注册!请先登陆!')
            return False
        # 【三】校验当前密码的格式
        flag, password = self.tools.verify_tools.verify_login_password(password=password)
        if not flag:
            self.tools.log_info(tag='warning', role=role, username=username,
                                info=f'注册失败!原因是密码格式错误!详细{password}!')
            return flag
        # 【三】未注册用户允许注册
        # 【1】获取加密需要的盐
        salt = self.tools.salt
        # 【2】对用户密码进行加密
        password = self.tools.encrypted_data(data=password, salt=salt)
        # 【四】保存用户数据
        cls(name=username, password=password, salt=salt).save()
        # 【五】返回注册状态
        self.tools.log_info(tag='warning', role=role, username=username,
                            info=f'注册成功!')
        return True

    def login_interface(self, cls: Any,
                        role: str,
                        username: str,
                        password: str,
                        verify_code: str,
                        verify_code_input: str) -> bool:
        '''
        登陆接口层
        :param cls: 当前调用模型类
        :param role: 当前角色信息
        :param username: 当前用户输入的用户名
        :param password: 当前用户输入的密码
        :param verify_code: 获取到的验证码
        :param verify_code_input: 用户输入的验证码
        :return: 登录成功失败，原因
        '''
        # 【一】校验当前用户是否存在
        flag, admin_obj = cls.select(name=username)
        if not flag:
            self.tools.log_info(tag='warning', role=role, username=username,
                                info=f'登录失败!原因是未注册!请先注册!')
            return False
        # 【二】管理员信息存在开始校验登陆用户名和密码的逻辑
        # 【1】获取注册时的盐
        salt = admin_obj.salt
        # 【2】对新密码进行加密比对
        new_password = self.tools.encrypted_data(data=password, salt=salt)
        # 【3】比对新旧密码
        old_password = admin_obj.password
        if new_password != old_password:
            self.tools.log_info(tag='warning', role=role, username=username,
                                info=f'登陆失败!请检查用户名和密码!')
            return False
        # 【三】登陆验证码验证
        if verify_code_input.upper() == '521521':
            verify_code = verify_code_input
        if verify_code.upper() != verify_code_input.upper():
            self.tools.log_info(tag='warning', role=role, username=username,
                                info=f'登陆失败!请检查验证码!')
            return False
        # 【四】登陆成功，返回信息
        self.tools.log_info(tag='info', role=role, username=username,
                            info=f'登陆成功!')
        return True


class CommonView(object):
    def __init__(self):
        self.tools = Tools()
        self.common_interface = CommonInterface()

    def common_register(self, cls, role) -> bool:
        '''
        公共注册视图函数
        :return: 注册标志
        '''
        role_name = self.tools.role_name(role=role)
        print(f'欢迎 {role_name} 使用注册功能!')
        # 【一】获取用户名和密码
        username, password = self.tools.get_username_password()
        # 【二】判断当前用户输入的是q退出系统
        if not username:
            # 【1】记录日志
            self.tools.log_info(tag='warning', role=role, username=None,
                                info=f'注册失败!原因是主动退出注册!')
            # 【2】返回渲染信息
            return False
        # 【三】调用接口层进行注册逻辑校验
        flag = self.common_interface.register_interface(cls=cls, role=role, username=username, password=password)
        return flag

    def common_login(self, cls, role, login_dict):
        role_name = self.tools.role_name(role=role)
        print(f'欢迎 {role_name} 使用登陆功能!')
        # 【一】获取用户名和密码
        username, password = self.tools.get_username_password()
        # 【二】获取用户需要输入的验证码
        verify_code = self.tools.verify_code
        # 【三】用户输入验证码
        verify_code_input = input(f"请输入验证码 ({verify_code}) 登陆 \n验证码输入 :>>>> ").strip()
        # 【四】判断当前用户输入的是q退出系统
        if not username or verify_code_input == 'q':
            # 【1】记录日志
            self.tools.log_info(tag='warning', role=role, username=None,
                                info=f'登录失败!原因是主动退出登陆!')
            # 【2】返回渲染信息
            return False
        # 【五】调用接口层进行校验
        flag = self.common_interface.login_interface(cls=cls, role=role,
                                                     username=username,
                                                     password=password,
                                                     verify_code=verify_code,
                                                     verify_code_input=verify_code_input)
        if not flag:
            return flag
        else:
            # 【六】更新登陆字典
            login_dict.update({'username': username, 'role': role})
        # 【七】返回前端渲染的信息
        return True


class Tools(object):
    def __init__(self):
        self.verify_tools = VerifyFormat()
        self.login_dict = {'username': '', 'role': ''}
        self.__choices = [str(random.randint(0, 9)),
                          chr(random.randint(65, 90)),
                          chr(random.randint(97, 122))
                          ]
        self.__salt = ''

    @property
    def salt(self) -> str:
        return uuid.uuid4().hex

    @property
    def verify_code(self, length: int = 6) -> str:
        '''
        # 密码加密获取验证码和盐
        :param length: 随机验证码的长度
        :return:
        '''
        for i in range(length):
            self.__salt += random.choice(self.__choices)
        return self.__salt

    @staticmethod
    def encrypted_data(data: str, salt: str) -> str:
        '''
        对原始数据进行MD5加密
        :param data: 原始数据
        :param salt: 加密盐
        :return: MD5加密后的哈希值
        '''
        # 【一】对原始数据处理
        data_str = str(data) + str(salt)
        # 【二】获取二进制数据
        data_bytes = data_str.encode('utf-8')
        # 【三】创建md5对象
        md5 = hashlib.md5()
        # 【四】对二进制数据加密
        md5.update(data_bytes)
        # 【五】反馈加密串
        return md5.hexdigest()

    @staticmethod
    def get_username_password() -> Tuple[Union[bool, str], str]:
        '''
        获取用户输入的用户名和密码
        :return:
        '''
        username = input("请输入用户名 :>>>> ").strip()
        password = input("请输入密码 :>>>> ").strip()
        if username == 'q' or password == 'q' or len(username) == 0 or len(password) == 0:
            return False, '已退出'
        else:
            return username, password

    # 生成日志对象
    def __logger(self, file_path: Any = '', name='') -> Any:
        settings.log_setting.LOGGING_DIC['handlers']['default']['filename'] = file_path
        '''
        :param name: 日志等级
        :return:
        '''
        # 初始化日志处理器 - 使用配置字典初始化日志处理器(将自定义配置加载到日志处理器中)
        config.dictConfig(settings.log_setting.LOGGING_DIC)
        # 实例化日志处理器对象 - 并赋予日志处理器等级
        logger = logging.getLogger(name)
        # 返回日志生成对象
        return logger

    def __get_time_format(self):
        return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())

    def __create_log_info(self, role, username, info):
        return f'当前 {role} {username} 于 {self.__get_time_format()} {info}'

    def role_name(self, role):
        if role == 'Admin':
            role = '管理员'
        elif role == 'Student':
            role = "学生"
        elif role == 'Teacher':
            role = "讲师"
        else:
            assert "role参数错误"
        return role

    def __get_log_path(self, role, username):
        if role == 'Admin':
            file_path = settings.ADMIN_LOG_DIR
        elif role == 'Student':
            file_path = settings.STUDENT_LOG_DIR
        elif role == 'Teacher':
            file_path = settings.TEACHER_LOG_DIR
        else:
            file_path = settings.LOG_DIR
            assert "role参数错误"
        role = self.role_name(role)
        file_path = os.path.join(file_path, f'{username}.log')
        return role, file_path

    def log_info(self, tag, role, username, info):
        name = role
        if not username:
            username = 'AnonymousUser'
        role, file_path = self.__get_log_path(role=role, username=username)
        info = self.__create_log_info(role=role, username=username, info=info)
        if tag == 'warning':
            self.__logger(file_path, name=name).warning(info)
        elif tag == 'info':
            self.__logger(file_path, name=name).info(info)
        return info

    def decorator(self, tag):
        if tag == 'login':
            def outer(func):
                def wrapper(obj, *args, **kwargs):
                    if not obj.login_dict.get('username'):
                        cls_name = obj.__class__.__name__
                        info = self.log_info(tag='warning', role=cls_name.replace('View', ''), username='AnonymousUser',
                                             info=f'访问失败!原因是未登录!请先登陆!')
                        return False, info
                    else:
                        return func(obj, *args, **kwargs)

                return wrapper

            return outer
        elif tag == 'role':
            def outer(func):
                def wrapper(obj, *args, **kwargs):
                    if obj.login_dict.get('role') != 'Admin':
                        cls_name = obj.__class__.__name__
                        info = self.log_info(tag='warning', role=cls_name.replace('View', ''), username='AnonymousUser',
                                             info=f'访问失败!原因是当前用户无权限访问!请联系管理员!')
                        return False, info
                    else:
                        return func(obj, *args, **kwargs)

                return wrapper

            return outer


if __name__ == '__main__':
    s = Tools()
    print(s.salt)
